package br.gfca.simplenet.test.chat.control;

import java.util.Scanner;

import javax.swing.SwingUtilities;

import br.gfca.simplenet.ClientImpl;
import br.gfca.simplenet.ClientListener;
import br.gfca.simplenet.ConnectionFailureEvent;
import br.gfca.simplenet.test.chat.MessageHeaders;
import br.gfca.simplenet.test.chat.ui.UserEntry;
import br.gfca.simplenet.util.Printer;

public class ClientChatController implements ClientListener {

	private ClientImpl netController;
	private ClientUiIf ui;
	private String nick;

	public ClientChatController(ClientUiIf ui) {
		this.netController = new ClientImpl(this);
		this.ui = ui;
	}

	@Override
	public void connected() {
		this.informNick();
		this.ui.connected(this.nick);
		this.ui.inform("You joined the chat.");
	}

	@Override
	public void connectionFailed(ConnectionFailureEvent cfe) {
		this.nick = null;
		this.ui.connectionFailed(cfe.getMessage());
	}

	@Override
	public void connectionLost() {
		this.nick = null;
		this.ui.connectionLost();
	}

	@Override
	public void messageReceived(String message) {
		if ( message.startsWith( MessageHeaders.MESSAGE_BROADCAST ) ) {
			this.ui.chatReceived( this.removeHeader( message, MessageHeaders.MESSAGE_BROADCAST ) );
		}
		else if ( message.startsWith( MessageHeaders.CONTROL_BROADCAST_USER_DISCONNECTED )) {
			String sid = this.removeHeader( message, MessageHeaders.CONTROL_BROADCAST_USER_DISCONNECTED );
			try {
				int id = Integer.parseInt( sid );
				this.ui.userDisconnected( id );
			}
			catch (NumberFormatException nfe) {
				// Invalid user id (not integer)
				Printer.getInstance().printError("Invalid disconnected user id: " + sid);
				Printer.getInstance().printStack( nfe );
			}
		}
		else if ( message.startsWith( MessageHeaders.CONTROL_BROADCAST_USER_KICKED )) {
			String sid = this.removeHeader( message, MessageHeaders.CONTROL_BROADCAST_USER_KICKED );
			try {
				int id = Integer.parseInt( sid );
				this.ui.userKicked( id );
			}
			catch (NumberFormatException nfe) {
				// Invalid user id (not integer)
				Printer.getInstance().printError("Invalid kicked user id: " + sid);
				Printer.getInstance().printStack( nfe );
			}
		}
		else if ( message.startsWith( MessageHeaders.CONTROL_BROADCAST_NEW_USER )) {
			message = this.removeHeader( message, MessageHeaders.CONTROL_BROADCAST_NEW_USER );
			try {
				int id = Integer.parseInt( message.substring(0, message.indexOf("_")) );
				String nick = message.substring( message.indexOf("_") + 1 );
				this.ui.newUser( new UserEntry( id, nick ) );
				this.ui.inform( nick + " joined the chat." );
			}
			catch (NumberFormatException nfe) {
				// Invalid user id (not integer)
				Printer.getInstance().printError("Invalid new user id: " + message.substring(0, message.indexOf("_")));
				Printer.getInstance().printStack( nfe );
			}
			catch (IndexOutOfBoundsException ioobe) {
				// Invalid message format
				Printer.getInstance().printError("Invalid message format for new user: " + message);
				Printer.getInstance().printStack( ioobe );

			}
		}
		else if ( message.startsWith( MessageHeaders.CONTROL_UNICAST_INFORM_USERS )) {
			message = this.removeHeader( message, MessageHeaders.CONTROL_UNICAST_INFORM_USERS );
			try {
				String[] indexes = message.substring(0, message.indexOf("#")).split("\\$");
				String nicks = message.substring( message.indexOf("#") + 1 );
				for (String s : indexes) {
					Scanner sc = new Scanner( s ).useDelimiter( "_" );
					String nick = nicks.substring( sc.nextInt(), sc.nextInt() );
					this.ui.newUser( new UserEntry(sc.nextInt(), nick) );
				}
			} catch (RuntimeException re) {
				// Invalid user id (not integer)
				Printer.getInstance().printError("Invalid inform users message: " + message);
				Printer.getInstance().printStack( re );
			}
		}
		else {
			// Should never get here
			Printer.getInstance().printError("Invalid/unexpected message received: " + message);
		}
	}

	@Override
	public void messageSent(final String message) {
		if ( message.startsWith( MessageHeaders.MESSAGE_UNICAST ) ) {
			// TODO imitar essa maneira de delegar para a UI
			SwingUtilities.invokeLater(new Runnable() {
				@Override
				public void run() {
					ui.ownChatReceived( removeHeader( message, MessageHeaders.MESSAGE_UNICAST ) );				}
			});
		}
		else if ( message.startsWith( MessageHeaders.CONTROL_UNICAST_INFORM_NICK )) {
			// Nothing to do
		}
		else {
			// Should never get here
			Printer.getInstance().printError("Invalid/unexpected message sent: " + message);
		}
	}
	
	@Override
	public void messageFailed(String message) {
		if ( message.startsWith( MessageHeaders.MESSAGE_UNICAST ) ) {
			this.ui.ownChatFailed( this.removeHeader( message, MessageHeaders.MESSAGE_UNICAST ) );
		}
		else if ( message.startsWith( MessageHeaders.CONTROL_UNICAST_INFORM_NICK )) {
			// Try again
			this.informNick();
		}
		else {
			// Should never get here
			Printer.getInstance().printError("Invalid/unexpected message sent, but failed: " + message);
		}
	}
	
	public void connect(String host, int port) {
		this.netController.connect(host, port);
	}

	public void disconnect() {
		this.nick = null;
		this.netController.disconnect();
	}

	public void sendMessage(String message) {
		this.netController.sendMessage( MessageHeaders.MESSAGE_UNICAST + this.nick + " says: " + message);
	}

	private void informNick() {
		this.netController.sendMessage(MessageHeaders.CONTROL_UNICAST_INFORM_NICK + this.nick);
	}

	private String removeHeader(String message, String messageHeader) {
		return message.substring( messageHeader.length() );
	}

	public void setNick(String nick) {
		this.nick = nick;
	}
}